import moment = require("moment");

import { v4 as uuidv4 } from "uuid";
import KeyValuePairsDb, { KVPair, OperableStoreShow, ResultType } from "logic/database/KeyValuePairsDb";
function to_new_id(set_count: number) {
    return `${set_count+1}0${uuidv4().replace(/[^\w]/g, '')}`;
}

/** 枚举: 复习状态 */
enum EReviewStatus {
    /** 未知 */
    unknown = 0,
    /** 复习完成 */
    complate = 1,
}

/** 读取参数: 复习记录 */
interface MReadParam_DayReviewRecord {
    /** 学习项Id */
    study_id?: string;
    /** 学习项Id列表 */
    study_ids?: string[];
    /** 周期标识 */
    day_cycle?: number;
    /** 复习状态 */
    review_status?: EReviewStatus;
    /** 复习时间 - 开始时间范围 */
    review_time_start?: string;
    /** 复习时间 - 结束时间范围 */
    review_time_end?: string;
}
/** 模型: 单日复习记录 */
interface MDayReviewRecord {
    /** 标识ID */
    id: string;
    /** 学习项Id */
    study_id: string;
    /** 周期标识 */
    day_cycle: number;
    /** 复习状态 */
    review_status: EReviewStatus;
    /** 复习时间 */
    review_time: string;
}

/**
 * 接口：复习记录
 */
interface IReviewRecordController extends ICRUDAsyncController<MDayReviewRecord, MReadParam_DayReviewRecord, string> {}

/**
 * 获取实例接口: 复习记录
 * @param store
 * @returns 实现实例
 */
function GetCase_IReviewRecordController(store: OperableStoreShow) : IReviewRecordController {
    return new ReviewRecordController(store);
}

export {
    MReadParam_DayReviewRecord,
    MDayReviewRecord,
    EReviewStatus,
    IReviewRecordController,
    GetCase_IReviewRecordController,
}

class ReviewRecordController implements IReviewRecordController {
    private readonly db: KeyValuePairsDb;
    private readonly store: OperableStoreShow;

    private readonly dict_time_list = {};
    private readonly dict_id_time = {};

    constructor(store: OperableStoreShow) {
        this.db = new KeyValuePairsDb();
        this.store = store;

        this.dict_time_list = {};
        this.dict_id_time = {};
    }

    private Cache_GetRecordList(time: moment.Moment): MDayReviewRecord[] {
        let self = this;
        let list:MDayReviewRecord[] = self.dict_time_list[time.format("YYYY-MM-DD")];
        return list;
    }
    private Cache_SetRecordList(time: moment.Moment, list:MDayReviewRecord[]): void {
        let self = this;
        self.dict_time_list[time.format("YYYY-MM-DD")] = list;
    }
    private Cache_GetTime(id: string) {
        let self = this;
        let time: moment.Moment = self.dict_id_time[id];
        return time;
    }
    private Cache_SetTime(id: string, time: moment.Moment) {
        let self = this;
        self.dict_id_time[id] = time;
        return time;
    }
    private GKey_ReviewRecordDayTime(time: moment.Moment) {
        return `EspinhoMemoryTable:ReviewRecord:${time.format("YYYYMMDD")}`;
    };
    private async UpdateDataList() {
        let self = this;

        let dict: KVPair = {};
        for (const time_str in self.dict_time_list) {
            let list: MDayReviewRecord[] = self.dict_time_list[time_str] || [];
            let time: moment.Moment = moment(time_str, "YYYY-MM-DD");
            let key: string = self.GKey_ReviewRecordDayTime(time);
            dict[key] = list;
        }

        let result:ResultType<number> = await self.db.Write(self.store.Name, dict);
        if (result.Code != 0) {
            console.log("写入数据失败", dict, self.store);
        }
    }


    async CreateOrUpdateAsync(m: MDayReviewRecord): Promise<ExecuteResult> {
        let self = this;
        console.log('EditStudy(CreateOrUpdate).m:', m);
        if (!m.id) {
            let time = moment();
            m.review_time = moment().format("YYYY-MM-DD HH:mm:ss");

            let list:MDayReviewRecord[] = self.Cache_GetRecordList(time) || [];
            let list_count: number = list.length;
            let new_id: string = `${time.format("YYYYMMDD")}0${list_count+1}0${uuidv4().replace(/[^\w]/g, '')}`;
            m.id = new_id;

            list.push(m);
            self.Cache_SetRecordList(time, list);
            self.Cache_SetTime(new_id, time);
        } else {
            let time = self.Cache_GetTime(m.id);
            let list:MDayReviewRecord[] = self.Cache_GetRecordList(time) || [];
            for (let i_list = 0; i_list < list.length; i_list++) {
                const element = list[i_list];
                if (element.id === m.id) {
                    let old_m: MDayReviewRecord = list[i_list];
                    m.review_time = old_m.review_time;
                    list[i_list] = m;
                    break;
                }
            }
            self.Cache_SetRecordList(time, list);
        }
        self.UpdateDataList();
        return { IsSuccess: true };
    }
    async ReadListAsync(param: MReadParam_DayReviewRecord): Promise<ExecuteResultData<MDayReviewRecord[]>> {
        let self = this;
        let r: ExecuteResultData<MDayReviewRecord[]> = {
            IsSuccess: false,
            Data: [],
        };

        // 分割时间条件, 依此查询每一条的记录, 条件为空则查最近一年的数据
        let time_start = moment().add(-1, 'years');
        let time_end = moment();
        if (param.review_time_start) {
            time_start = moment(param.review_time_start);
        }
        if (param.review_time_end) {
            time_end = moment(param.review_time_end);
        }

        // 结果, key:时间 value:列表
        let rdict_time_list = {};
        const rdict_time_format: string = "YYYY-MM-DD";
        // 筛选需要查询的时间键名
        let keys: string[] = [];
        let keys_time: moment.Moment[] = [];

        do {
            // 获取当前时间
            let time_day_item_str = time_start.format("YYYY-MM-DD");
            let time_day_item: moment.Moment = moment(time_day_item_str);

            let time_cache_list: MDayReviewRecord[] = self.Cache_GetRecordList(time_day_item) || [];
            if (time_cache_list.length > 0) {
                // 在当前缓存中存在就读取当前缓存即可
                rdict_time_list[time_day_item.format(rdict_time_format)] = time_cache_list;
            } else {
                // 不存在则需要读取接口
                let key = self.GKey_ReviewRecordDayTime(time_day_item);
                keys.push(key);
                keys_time.push(time_day_item);
            }

            // 累加一天时间
            time_start = time_start.add(1, "days");
        } while (time_start.isSameOrBefore(time_end))

        // 判断需读取键列表不为空, 访问数据接口
        if (keys.length > 0) {
            let result:ResultType<KVPair> = await self.db.Reads(self.store.Name, keys);
            if (result.Code != 0) {
                r.Message = "查询结果为空";
                console.log(r, keys, self.store);
                return r;
            }
            let dict: KVPair = result.Data;
            for (let i_key = 0; i_key < keys.length; i_key++) {
                const key: string = keys[i_key];
                let key_time: moment.Moment = keys_time[i_key];
                let list: MDayReviewRecord[] = dict[key] || [];
                // 写入缓存
                self.Cache_SetRecordList(key_time, list);
                // 写入结果
                rdict_time_list[key_time.format(rdict_time_format)] = list;
            }
        }
        let rlist = [];
        for (const time in rdict_time_list) {
            let list = rdict_time_list[time];
            rlist = rlist.concat(list);
        }

        r.IsSuccess = true;
        r.Data = rlist;
        r.Data = self.filterResultList(r.Data, param);
        return r;
    }
    private filterResultList(source: MDayReviewRecord[], param: MReadParam_DayReviewRecord) {
        let rlist: MDayReviewRecord[] = [];

        for (let i = 0; i < source.length; i++) {
            const m = source[i];

            let wheres:boolean[] = [];

            if (param.study_id) {
                wheres.push((m.study_id || "").indexOf(param.study_id) > -1);
            }
            if (param.study_ids && param.study_ids.length > 0) {
                wheres.push(param.study_ids.findIndex(b => b == m.study_id) > -1);
            }
            if (param.day_cycle) {
                wheres.push(param.day_cycle === m.day_cycle);
            }
            if (param.review_status) {
                wheres.push(param.review_status === m.review_status);
            }
            if (param.review_time_start) {
                wheres.push(moment(param.review_time_start).isBefore(moment(m.review_time)));
            }
            if (param.review_time_end) {
                wheres.push(moment(m.review_time).isBefore(moment(param.review_time_end)));
            }

            let is_show: boolean = true;
            for (let i_where = 0; i_where < wheres.length; i_where++) {
                const w = wheres[i_where];
                if (w == null) {
                    continue;
                }
                is_show = is_show && w;
            }
            if (is_show) {
                rlist.push(m);
            }
        }
        return rlist;
    }
    async DeletesAsync(ids: string[]): Promise<ExecuteResultData<number>> {
        let self = this;
        let oper_count = 0;
        if (!ids || ids.length <= 0) {
            return { IsSuccess: false, Message: "删除ID列表为空" };
        }
        for (let i_id = 0; i_id < ids.length; i_id++) {
            const id: string = ids[i_id];
            let time: moment.Moment = self.Cache_GetTime(id);
            let list: MDayReviewRecord[] = self.Cache_GetRecordList(time) || [];
            if (list.length <= 0) {
                continue;
            }
            for (let index_m = list.length -1; index_m >= 0; index_m--) {
                const m = list[index_m];
                let is_have: boolean = false;
                for (let index_id = 0; index_id < ids.length; index_id++) {
                    const del_id = ids[index_id];
                    if (m.id === del_id) {
                        is_have = true;
                        break;
                    }
                }
                if (is_have) {
                    list.splice(index_m, 1);
                    oper_count++;
                }
            }
            self.Cache_SetRecordList(time, list);
        }

        self.UpdateDataList();
        return { IsSuccess: true, Data: oper_count };
    }
}
